import { defineComponent, ref, Ref, provide, inject, computed, watch, nextTick } from "vue";
import styles from "./lForm.module.less";
import _ from "lodash";
import { message } from "ant-design-vue";
import validateInstance from "@/libs/validate/validate";

// 验证广播
const validity_broad = ref(0);

// 表单主体
export default defineComponent({
  name: 'LForm',
  props: {
    // 表单数据--嵌套结构须转为扁平结构
    modelValue: {
      type: Object,
      default: () => { },
    },
    // 验证规则
    rules: {
      type: Object,
      default: () => { },
    },
    // 按钮组
    btns: {
      type: Array,
      default: () => [],
    },
    // 内联
    inline: {
      type: Boolean,
      default: false,
    },
    // item间距
    gap: {
      type: Number,
      default: 20,
    }
  },
  emits: ['update:modelValue', 'confirm', 'cancel'],
  setup(props, { slots, emit }) {
    // 表单提交中
    const confirmLoading = ref(false);
    // 按钮
    let btn_arr: any[] = [];

    // 表单原始数据
    const ori_form = _.cloneDeep(props.modelValue);

    // 表单行配置
    const form_sets: Ref<{ [key: string]: any }> = ref({});
    const initErrorState = (obj: any, mObj: any) => {
      Object.keys(obj).forEach((x: any) => {
        if (obj[x] instanceof Object) {
          mObj[x] = {
            required_error: false,
            match_error: false,
            regExp_error: false,
          }
          initErrorState(obj[x], mObj[x]);
        } else {
          mObj[x] = {
            required_error: false,
            match_error: false,
            regExp_error: false,
          }
        }
      })
    }
    initErrorState(props.modelValue, form_sets.value);
    // 重置数据
    const reset = () => {
      emit('update:modelValue', { ...ori_form });
    }
    // 判断无验证报错
    const checkForm = (obj: any) => {
      let bol = true;
      const checkEach = (mObj: any) => {
        Object.values(mObj).forEach((x: any) => {
          if (!(!x.match_error && !x.regExp_error && !x.required_error)) {
            bol = false;
          }
          Object.values(x).forEach(y => {
            if (y instanceof Object) {
              checkEach(y);
            }
          })
        })
      }
      checkEach(obj)
      return bol;
    }
    // 提交表单
    const confirm = () => {
      validity_broad.value++;
      nextTick(() => {
        if (checkForm(form_sets.value)) {
          confirmLoading.value = true;
          setTimeout(() => {
            emit('confirm', {
              form: props.modelValue,
              done: () => {
                confirmLoading.value = false;
              }
            });
          }, 1000);
        } else {
          message.error('表单填入数据有误,请完善表单!');
        }
      })
    }
    // 点击取消
    const cancel = () => {
      emit('cancel');
    }

    // 所有预设按钮
    const allBtns = [
      {
        // 取消按钮
        key: 'cancel',
        el: () => <a-button onClick={() => { cancel() }}>取消</a-button>,
      },
      {
        // 重置按钮
        key: 'reset',
        el: () => <a-button type="primary" class="et-btn-warning" onClick={() => { reset() }}>重置</a-button>,
      },
      {
        // 确认按钮
        key: 'confirm',
        el: () => <a-button loading={confirmLoading.value} type="primary" onClick={() => { confirm() }}>确认</a-button>,
      },
    ];

    // 默认按钮组
    const defaultBtns = ['cancel', 'reset', 'confirm'];

    //最终按钮组key
    let btn_keys = props.btns ? props.btns : defaultBtns;
    //最终按钮组
    btn_keys.forEach(x => {
      if (typeof x === 'string') {
        allBtns.map(y => y.key).includes(x) && btn_arr.push(allBtns.find(y => y.key === x)?.el);
      } else {
        btn_arr.push(x);
      }
    })
    const Components = () => (
      <>
        <div class={[styles.form, props.inline && styles.inline]}>
          {/* 表单项 */}
          {slots.default && slots.default({})}
          {
            (slots.btns || btn_arr.length > 0) && <div class={styles['form-btns']} style={{ marginTop: `${props.gap}px` }}>
              {/* 按钮设置优先性 插槽 > props > 默认 */}
              {slots.btns ? slots.btns() : btn_arr.map(x => <x />)}
            </div>
          }
        </div>
      </>
    );
    provide('form', computed(() => props.modelValue));
    provide('form_sets', form_sets);
    provide('rules', computed(() => props.rules));
    provide('gap', computed(() => props.gap));
    provide('inline', computed(() => props.inline));
    return {
      Components,
    }
  },
  render() {
    return <this.Components />
  },
})

// 表单项
export const LFormItem = defineComponent({
  name: 'lFormItem',
  props: {
    // 标头
    label: {
      required: true,
    },
    // 对应的参数key
    prop: {
      type: String,
      required: true,
    },
    // 占据位置
    span: {
      type: Number,
      default: 24,
    },
  },
  setup(props, { slots }) {
    const form: any = inject('form');
    const form_sets: any = inject('form_sets');
    const rules: any = inject('rules');
    const gap: any = inject('gap');
    const inline: any = inject('inline');
    // 规则对象遍历
    const deepRuleEach: any = (obj: any, str: string) => {
      return obj?.[str];
    }
    // 表单数据遍历
    const deepFormEach: any = (obj: any, str: string) => {
      let mObj = obj || null;
      let arr = str?.split?.('.') || [];
      const doDeepFormEach = (i: number) => {
        mObj = mObj[arr[i]];
        if (i < arr.length - 1) {
          i = i + 1;
          doDeepFormEach(i);
        }
      }
      arr.length > 0 && doDeepFormEach(0);
      return mObj;
    }
    // 当前组件是否为必填项
    const required = computed(() => {
      return deepRuleEach(rules?.value, props.prop)?.some((x: any) => x.required);
    })
    // 当前组件是否需要显示必填报错信息
    const required_error_show = computed(() => {
      return deepFormEach(form_sets?.value, props.prop)?.required_error;
    })

    // 当前组件的函数验证规则
    const match = computed(() => {
      return deepRuleEach(rules?.value, props.prop)?.find((x: any) => x.type === 'match')?.match;
    })
    // 当前组件是否需要显示验证报错信息
    const match_error_show = computed(() => {
      return deepFormEach(form_sets?.value, props.prop)?.match_error;
    })

    // 当前组件的正则表达式验证规则
    const regExp: any = computed(() => {
      return deepRuleEach(rules?.value, props.prop)?.find((x: any) => x.type === 'regExp')?.regExp;
    })
    // 当前组件是否需要显示正则表达式验证报错信息
    const regExp_error_show = computed(() => {
      return deepFormEach(form_sets?.value, props.prop)?.regExp_error;
    })

    // 监听Form组件广播信息,触发验证
    watch(validity_broad, () => {
      checkValidity();
    })
    // 监听表单信息
    watch(() => deepFormEach(form.value, props.prop), (nv) => {
      checkValidity();
    })
    // 验证输入值
    const checkValidity = () => {
      if (required.value) {
        if (!deepFormEach(form?.value, props.prop)) {
          deepFormEach(form_sets?.value, props.prop).required_error = true;
        } else {
          deepFormEach(form_sets?.value, props.prop).required_error = false;
        }
      }
      if (match.value) {
        deepFormEach(form_sets?.value, props.prop).match_error = !validateInstance.test(deepFormEach(form?.value, props.prop), match.value);
      }
      if (regExp.value) {
        deepFormEach(form_sets?.value, props.prop).regExp_error = !validateInstance.test(deepFormEach(form?.value, props.prop), regExp.value);
      }
    }
    // 报错提示,优先级 required > match > regExp
    const ErrTips = () => (
      <>
        {
          required_error_show.value ?
            (
              <span class={styles['form-item-required-tips']}>
                {deepRuleEach(rules?.value, props.prop)?.find((x: any) => x.required).message}
              </span>
            ) : (
              match_error_show.value ?
                (
                  <span class={styles['form-item-required-tips']}>
                    {deepRuleEach(rules?.value, props.prop)?.find((x: any) => x.type === 'match').message}
                  </span>
                ) : (
                  regExp_error_show.value &&
                  (
                    <span class={styles['form-item-required-tips']}>
                      {deepRuleEach(rules?.value, props.prop)?.find((x: any) => x.type === 'regExp').message}
                    </span>
                  )
                )
            )
        }
      </>
    )

    const Components = () => (
      <>
        <div class={styles['form-item']} style={{
          width: `calc(100% * (${props.span / 24}))`,
          marginTop: `${gap.value}px`,
          padding: `0 ${inline.value && `${gap.value / 2}px`}`
        }}>
          <div class={[styles['form-item-label'], required.value && styles['form-item-label-required']]}>
            {
              typeof props.label === 'function' ? props.label() : props.label
            }
          </div>
          <div class={styles['form-item-value-container']}>
            <div class={styles['form-item-value']}>
              {slots.default && slots.default()}
            </div>
            <ErrTips />
          </div>
        </div>
      </>
    );
    return {
      Components,
    }
  },
  render() {
    return <this.Components />
  },
})